grammar tiny;

options
{
    language=C;
}

// Tokens used in the grammar. Every symbol that would be parsed is created as a token
tokens
{	PLUS	= '+';
	MINUS	= '-';
	MULT	= '*';
	DIV	= '/';
	ST	= '<';
	BT	= '>';
	EQ	= '==';
	ASOP	= '=';
	LAND	= '&&';
	LOR 	= '||';
	LXOR	= '^^';
	LNOT	= '!';
	BAND	= '&';
	BOR 	= '|';
	BXOR	= '^';
	BNOT	= '~';
	INT	= 'int';
	BOOL	= 'bool';
	VOID 	= 'void';
	CONST	= 'const';
	SEMIC	= ';';
	COMMA	= ',';
	IF	= 'if';
	WHILE	= 'while';
	ELSE	= 'else';
	FOR	= 'for';
	OPPAREN	= '(';
	CLPAREN	= ')';
	OPBRACE	= '{';
	CLBRACE	= '}';
	OPBRAK  = '[';
	CLBRAK  = ']';
	RETURN	= 'return';
	TRUE	= 'true';
	FALSE	= 'false';
	COLON 	= ':';
	PROP 	= 'PROP';
}

// These are the include files that would be needed to run the code
// hash.h is a custom-made code to create the hash table
@parser::includes
{
	#include "hash.h"
	#include <string.h>
}

// These are the global variables used throughout the grammar by all the rules
@members
{
	struct hashTable h[SIZE_HASH_MAP]; // The global hash table
	struct node* g[SIZE_HASH_MAP]; // The graph that will be created
	struct node* c[SIZE_HASH_MAP]; // An intermediate graph that will hold the children of a created node
	int gNb = 0, cNb = 0, hNb = 0; // The counters of the graphh, intermediate graph and the hash
	int scopeCounter = 0, varIndex = 0; 
	int IDMatrix[SIZE_HASH_MAP][SIZE_HASH_MAP] = {-1};
	char* currentID; // This is the current ID that is used so that we don't get errors in b = a + b
	int currentNode; // This is current nodeNb of the current ID that is located in the LHS
}	

/*------------------------------------------------------------------
 * PARSER RULES
 *------------------------------------------------------------------*/

program [struct node **graphNode, int *propNb]
@init {
	int i = 0, j = 0, k = 0;
	for (i; i<SIZE_HASH_MAP; i++)
	{
		h[i].nodePtr = (struct node*)malloc(SIZE_HASH_MAP*sizeof(struct node));
		h[i].nodePtr = '\0';
		g[i] = (struct node*)malloc(SIZE_HASH_MAP*sizeof(struct node));
		c[i] = (struct node*)malloc(SIZE_HASH_MAP*sizeof(struct node));
		graphNode[i] = (struct node*)malloc(SIZE_HASH_MAP*sizeof(struct node));
		for (k=0;k<SIZE_HASH_MAP;k++)
			IDMatrix[i][k] = -1;
	}
}	
	:	
		(fundef | statement)* property
		{
		for (j;j<SIZE_HASH_MAP;j++)
		{
			graphNode[j] = g[j];
		}
		graphNode = g;
		propNb[0] = $property.nodeNb;
		};

property returns [int nodeNb]
	:	PROP COLON bexpr
		{ $nodeNb = $bexpr.nodeNb;
		};

paramdec:	(CONST)? (INT | BOOL) (BAND)? ID (ASOP NUMBER)? {printf("paramdec ");};

boolIDdec returns [int nodeNb]
@init {
int childNode = 0;}
@after {
childNode = 0;}
	:	ID {
			struct node* temp = (struct node*)malloc(sizeof(struct node)), *hashNode = (struct node*)malloc(sizeof(struct node));
			temp = linearProbingSearch(h,$ID.text->chars);
			if (temp=='\0') {
				initnode(g[gNb],$ID.text->chars, 1, gNb);
				hashNode->name = g[gNb]->name;
				hashNode->type = scopeCounter; //columns of the matrix
				hashNode->sequence = varIndex++; //rows of the matrix, increment for a new variable
				linearProbingInsert(h, hashNode);
				childNode = gNb;
				IDMatrix[hashNode->sequence][hashNode->type] = gNb;
				printf("node \%d: \%s\n",gNb,$ID.text->chars);
				gNb++;}
			else { 
				if (IDMatrix[temp->sequence][scopeCounter]==-1) {
					initnode(g[gNb],$ID.text->chars, 1, gNb);
					childNode = gNb;
					IDMatrix[temp->sequence][scopeCounter] = gNb;
					printf("node \%d: \%s\n",gNb,$ID.text->chars);
					IDMatrix[temp->sequence][scopeCounter] = gNb;
					gNb++;
				}
			
				else
				{
					printf("Line \%d, Position \%d : Duplicate declaration for \%s!\n", $ID.line, $ID.pos,temp->name);
					exit(1);
				}
			}
			}
		ASOP {
			initnode(g[gNb],$ASOP.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$ASOP.text->chars);
			$nodeNb = gNb;
			gNb++;}
		bexpr {
			c[cNb] = g[childNode];
			c[cNb+1] = g[$bexpr.nodeNb];
			c[cNb+2] = '\0';		
			addnode('\0',c,g[$nodeNb]);
			};
	
intIDdec returns [int nodeNb]
@init {
int childNode = 0;
}
@after {
childNode = 0;}
	:	ID {
			struct node* temp = (struct node*)malloc(sizeof(struct node)), *hashNode = (struct node*)malloc(sizeof(struct node));
			temp = linearProbingSearch(h,$ID.text->chars);
			if (temp=='\0') {
				initnode(g[gNb],$ID.text->chars, 1, gNb);
				hashNode->name = g[gNb]->name;
				hashNode->type = scopeCounter; //columns of the matrix
				hashNode->sequence = varIndex++; //rows of the matrix, increment for a new variable
				linearProbingInsert(h, hashNode);
				$nodeNb = gNb;
				childNode = gNb;
				IDMatrix[hashNode->sequence][hashNode->type] = gNb;
				printf("node \%d: \%s\n",gNb,$ID.text->chars);
				gNb++;}
			else { 
				if (IDMatrix[temp->sequence][scopeCounter]==-1) {
					initnode(g[gNb],$ID.text->chars, 1, gNb);
					$nodeNb = gNb;
					childNode = gNb;
					IDMatrix[temp->sequence][scopeCounter] = gNb;
					printf("node \%d: \%s\n",gNb,$ID.text->chars);
					IDMatrix[temp->sequence][scopeCounter] = gNb;
					gNb++;
				}		
				else
				{
					printf("Line \%d, Position \%d : Duplicate declaration for \%s!\n", $ID.line, $ID.pos,temp->name);
					exit(1);	
				}
			}
			}
		(ASOP {
			initnode(g[gNb],$ASOP.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$ASOP.text->chars);
			$nodeNb = gNb;
			gNb++;}
		expr {
			c[cNb] = g[childNode];
			c[cNb+1] = g[$expr.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);
			})?;

boolarraydec
	:	ID OPBRAK CLBRAK ASOP OPBRACE (TRUE | FALSE) (COMMA (TRUE | FALSE))* CLBRACE 
		| ID OPBRAK (NUMBER | ID) CLBRAK (ASOP OPBRACE (TRUE | FALSE) (COMMA (TRUE | FALSE))* CLBRACE)?;
		
intarraydec
	:	ID OPBRAK CLBRAK ASOP OPBRACE NUMBER (COMMA NUMBER)* CLBRACE 
		| ID OPBRAK (NUMBER | ID) CLBRAK (ASOP OPBRACE NUMBER (COMMA NUMBER)* CLBRACE)?;

booldec	returns [int nodeNb]
@init {
int i = 0,childNb = 0;
int decs[SIZE_HASH_MAP] = {0};}
@after {
i = 0;
childNb = 0;}
	:	{
			initnode(g[gNb],"decL", 3, gNb);
			printf("node \%d: \%s\n",gNb,g[gNb]->name);
			$nodeNb = gNb;
			gNb++;}
		(CONST)? BOOL (id1=ID 
		{
			struct node* temp = (struct node*)malloc(sizeof(struct node)), *hashNode = (struct node*)malloc(sizeof(struct node));
			temp = linearProbingSearch(h,$id1.text->chars);
			if (temp=='\0') {
				initnode(g[gNb],$id1.text->chars, 1, gNb);
				hashNode->name = g[gNb]->name;
				hashNode->type = scopeCounter; //columns of the matrix
				hashNode->sequence = varIndex++; //rows of the matrix, increment for a new variable
				linearProbingInsert(h, hashNode);
				IDMatrix[hashNode->sequence][hashNode->type] = gNb;
				decs[childNb++] = gNb;
				printf("node \%d: \%s\n",gNb,$id1.text->chars);
				gNb++;}
			else { 
				if (IDMatrix[temp->sequence][scopeCounter]==-1) {
					initnode(g[gNb],$id1.text->chars, 1, gNb);
					IDMatrix[temp->sequence][scopeCounter] = gNb;
					printf("node \%d: \%s\n",gNb,$id1.text->chars);
					IDMatrix[temp->sequence][scopeCounter] = gNb;
					decs[childNb++] = gNb;
					gNb++;
				}		
				else
				{
					printf("Line \%d, Position \%d : Duplicate declaration for \%s!\n", $id1.line, $id1.pos,temp->name);
					exit(1);
				}
			}		
		}
		| b1 = boolIDdec 
		{
			decs[childNb++] = $b1.nodeNb;
		}
		| boolarraydec) (COMMA (id2=ID 
		{
			struct node* temp = (struct node*)malloc(sizeof(struct node)), *hashNode = (struct node*)malloc(sizeof(struct node));
			temp = linearProbingSearch(h,$id2.text->chars);
			if (temp=='\0') {
				initnode(g[gNb],$id2.text->chars, 1, gNb);
				hashNode->name = g[gNb]->name;
				hashNode->type = scopeCounter; //columns of the matrix
				hashNode->sequence = varIndex++; //rows of the matrix, increment for a new variable
				linearProbingInsert(h, hashNode);
				IDMatrix[hashNode->sequence][hashNode->type] = gNb;
				decs[childNb++] = gNb;
				printf("node \%d: \%s\n",gNb,$id2.text->chars);
				gNb++;}
			else { 
				if (IDMatrix[temp->sequence][scopeCounter]==-1) {
					initnode(g[gNb],$id2.text->chars, 1, gNb);
					IDMatrix[temp->sequence][scopeCounter] = gNb;
					decs[childNb++] = gNb;
					printf("node \%d: \%s\n",gNb,$id2.text->chars);
					gNb++;
				}		
				else
				{
					printf("Line \%d, Position \%d : Duplicate declaration for \%s!\n", $id2.line, $id2.pos,temp->name);
					exit(1);
				}
			}
		}
		| b2 = boolIDdec {
			decs[childNb++] = $b2.nodeNb;
		}
		| boolarraydec))* SEMIC
		{
			for (i;i < childNb; i++)
			{
				c[i] = g[decs[i]];
			}
			c[childNb] = '\0';
			addnode('\0', c, g[$nodeNb]);
		};

intdec returns [int nodeNb]
@init {
int i = 0,childNb = 0;
int decs[SIZE_HASH_MAP] = {0};
//clearnode(c);
}
@after {
i = 0;
childNb = 0;
}
	:	{
			initnode(g[gNb],"decL", 3, gNb); 
			printf("node \%d: \%s\n",gNb,g[gNb]->name);
			$nodeNb = gNb;
			gNb++;} 			
		(CONST)? INT (i1 = intIDdec { 
			decs[childNb++] = $i1.nodeNb;}
		| intarraydec 
		) (COMMA (i2 = intIDdec { 
			decs[childNb++] = $i2.nodeNb;}
		| intarraydec 
		))* SEMIC
		{
			for (i;i < childNb; i++)
				c[i] = g[decs[i]];
			c[childNb] = '\0';
			addnode('\0', c, g[$nodeNb]);
		};

declaration returns [int nodeNb]
	:	booldec {$nodeNb = $booldec.nodeNb;}
		| intdec {$nodeNb = $intdec.nodeNb;};

assignment returns [int nodeNb]
@init {
	int subnodeNb, i; 
	currentID=""; 	// used to keep track of the LHS ID of an assignment in order
		     	//to make sure in the assignment b = a + b the RHS b is the node of b that 
			//was created prior to enterint this ruke
	currentNode = 0;
}
@after {
	currentID = "";
	currentNode = 0;
}
	:	ID (OPBRAK expr CLBRAK)? {
			currentID = $ID.text->chars;
			struct node* temp = (struct node*)malloc(sizeof(struct node)), *hashNode = (struct node*)malloc(sizeof(struct node));
			temp = linearProbingSearch(h,$ID.text->chars);
			if (temp=='\0') {
				printf("Line \%d, Position \%d : Unknown identifier \%s!\n",$ID.line, $ID.pos,$ID.text->chars);
				exit(1);
			}
			else if (IDMatrix[temp->sequence][scopeCounter]==-1) {
				i = scopeCounter-1;
				while (i >= 0) {
					if (IDMatrix[temp->sequence][i]!=-1) {
						initnode(g[gNb],$ID.text->chars, 1, gNb);
						subnodeNb = gNb;
						currentNode = IDMatrix[temp->sequence][temp->type];
						IDMatrix[temp->sequence][temp->type] = gNb;
						printf("node \%d: \%s\n",gNb,$ID.text->chars);
						gNb++;	
						break;
					}
					i--;
				}	
				if (i < 0) {
					printf("Line \%d, Position \%d : Unknown identifier \%s!\n",$ID.line, $ID.pos,$ID.text->chars);
					exit(1);
				}
			}
			else { 
				initnode(g[gNb],$ID.text->chars, 1, gNb);
				subnodeNb = gNb;
				currentNode = IDMatrix[temp->sequence][temp->type];
				IDMatrix[temp->sequence][temp->type] = gNb;
				printf("node \%d: \%s\n",gNb,$ID.text->chars);
				gNb++;		
			}
			} 
		ASOP {
			initnode(g[gNb],$ASOP.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$ASOP.text->chars);
			$nodeNb = gNb;
			gNb++;}
		(b1 = expr | TRUE | FALSE) 
		{
			c[cNb] = g[subnodeNb];
			c[cNb+1] = g[$b1.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[subnodeNb+1]);
		};
	
fragment
fordec	:	((INT)? ID (ASOP expr)?)? {printf("fordec ");};

statement returns [int nodeNb]
	:	((ifstatement)=>ifstatement {$nodeNb = $ifstatement.nodeNb;}
		| (forstatement)=>forstatement
		| (whilestatement)=>whilestatement {$nodeNb = $whilestatement.nodeNb;}
		| (assignment {$nodeNb = $assignment.nodeNb;}) SEMIC
		| SEMIC| declaration {$nodeNb = $declaration.nodeNb;}
		| funcall SEMIC);
	
statementlist returns [int nodeNb] 
@init {
int i = 0,childNb = 0;
int stmts[SIZE_HASH_MAP] = {0};
}
@after {
i = 0;
childNb = 0;
}
	:	{
			initnode(g[gNb],"stmtL", 3, gNb); 
			printf("node \%d: \%s\n",gNb,g[gNb]->name);
			$nodeNb = gNb;
			gNb++;} 
		(statement {
			stmts[childNb++] = $statement.nodeNb;
		})+ 
		{
			for (i;i < childNb; i++)
				c[i] = g[stmts[i]];
			c[childNb] = '\0';
			addnode('\0', c, g[$nodeNb]);
		};
	
expr	returns [int nodeNb] 
	:	b1 = iterm {
			$nodeNb = $b1.nodeNb;
		} 
		( ( PLUS {
			initnode(g[gNb],$PLUS.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$PLUS.text->chars);
			$nodeNb = gNb;
			gNb++;}  
		| MINUS {
			initnode(g[gNb],$MINUS.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$MINUS.text->chars);
			$nodeNb = gNb;
			gNb++;}
		 )  b2 = iterm {
		 	c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);	
		 }
		    )*;

iterm	returns [int nodeNb] 
	:	b1 = ifactor {
			$nodeNb = $b1.nodeNb;
		} 
		( ( MULT {
			initnode(g[gNb],$MULT.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$MULT.text->chars);
			$nodeNb = gNb;
			gNb++;}  
		| DIV {
			initnode(g[gNb],$DIV.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$DIV.text->chars);
			$nodeNb = gNb;
			gNb++;} 
		)  b2 = ifactor {
			c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);			
		}
		)*;

ifactor	returns [int nodeNb]
	:	b1 = isub1factor {
			$nodeNb = $b1.nodeNb;
		}
		( BOR {
			initnode(g[gNb],$BOR.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$BOR.text->chars);
			$nodeNb = gNb;
			gNb++;} 
		b2 = isub1factor {
			c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);
		}
		)*;

isub1factor returns [int nodeNb]
	:	b1 = isub2factor {
			$nodeNb = $b1.nodeNb;
		}
		( BXOR {
			initnode(g[gNb],$BXOR.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$BXOR.text->chars);
			$nodeNb = gNb;
			gNb++;}
		b2 = isub2factor {
			c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);
		}	
		)*;
	
isub2factor returns [int nodeNb]
	:	b1 = isub3factor {
			$nodeNb = $b1.nodeNb;
		} 
		( BAND  {
			initnode(g[gNb],$BAND.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$BAND.text->chars);
			$nodeNb = gNb;
			gNb++;}  
		b2 = isub3factor 
		{
			c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);			
		}
		)*;

isub3factor returns [int nodeNb]
@init {
// lnotExit is used to keep track of the presence of MINUS and BNOT so that the corresponding node is created and
// the node containing NUMBER of ID is created and made the child of the MINUS or BNOT node
	int lnotExist = 0, i;
}
	:	(MINUS {
			lnotExist = gNb;
			gNb++;
			initnode(g[lnotExist],$MINUS.text->chars, 2, lnotExist); 
			printf("node \%d: \%s\n",gNb,$MINUS.text->chars);
		}
		| BNOT {
			lnotExist = gNb;
			gNb++;
			initnode(g[lnotExist],$BNOT.text->chars, 2, lnotExist); 
			printf("node \%d: \%s\n",gNb,$BNOT.text->chars);
		})? 
		(NUMBER {
			initnode(g[gNb],$NUMBER.text->chars, 0, gNb); 
			printf("node \%d: \%s\n",gNb,$NUMBER.text->chars);
			$nodeNb = gNb;
			gNb++;}
		|ID {	
			if (strcmp($ID.text->chars,currentID)==0) {
				$nodeNb = currentNode;
				printf("node \%d: \%s\n",$nodeNb,$ID.text->chars);
			}
			else {
				struct node* temp = (struct node*)malloc(sizeof(struct node)), *hashNode = (struct node*)malloc(sizeof(struct node));
				temp = linearProbingSearch(h,$ID.text->chars);
				if (temp=='\0') {
					printf("Line \%d, Position \%d : Unknown identifier \%s!\n",$ID.line, $ID.pos,$ID.text->chars);
					exit(1);
				}
				else if (IDMatrix[temp->sequence][scopeCounter]==-1) {
					i = scopeCounter-1;
					while (i >= 0) {
						if (IDMatrix[temp->sequence][i]!=-1) {
							$nodeNb = IDMatrix[temp->sequence][i];
							printf("node \%d: \%s\n",$nodeNb,$ID.text->chars);
							break;
						}
						i--;
					}	
					if (i < 0) {
						printf("Line \%d, Position \%d : Unknown identifier \%s!\n",$ID.line, $ID.pos,$ID.text->chars);
						exit(1);
					}
				}
				else {
					$nodeNb = IDMatrix[temp->sequence][scopeCounter];
					printf("node \%d: \%s\n",$nodeNb,$ID.text->chars);			
				}
			}
		}
		| funcall)
		{
			if (lnotExist)
			{
				$nodeNb = lnotExist;
				c[cNb] = g[lnotExist+1];
				c[cNb+2] = '\0';
				addnode('\0',c,g[lnotExist]);		
			}
		}
		;


bexpr returns [int nodeNb] 	
	:	b1 = bterm 
		{
			$nodeNb = $b1.nodeNb;
		}
		(LOR {
			initnode(g[gNb],$LOR.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$LOR.text->chars);
			$nodeNb = gNb;
			gNb++;}
		b2 = bterm {
			c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);
		}
		)*;

bterm returns [int nodeNb]	
	:	b1 = bfactor 
		{
			$nodeNb = $b1.nodeNb;
		}
		(LAND {
			initnode(g[gNb],$LAND.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$LAND.text->chars);
			$nodeNb = gNb;
			gNb++;}
		b2 = bfactor {
			c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);
		}
		)*;

bfactor	returns [int nodeNb]
	:	b1 = bsub1factor
		{
			$nodeNb = $b1.nodeNb;
		}
		(LXOR  {
			initnode(g[gNb],$LXOR.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$LXOR.text->chars);
			$nodeNb = gNb;
			gNb++;} 
		b2 = bsub1factor 
		{
			c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);
		}
		)*;
	

bsub1factor returns [int nodeNb]
	:	b1 = bsub2factor 
		{
			$nodeNb = $b1.nodeNb;
		}
		( EQ {
			initnode(g[gNb],$EQ.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$EQ.text->chars);
			$nodeNb = gNb;
			gNb++;}  
		b2 = bsub2factor
		{
			c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);
		}	
		)*;
	
bsub2factor returns [int nodeNb] 
	:	b1 = bsub3factor {
			$nodeNb = $b1.nodeNb;
		} 
		( (ST 
		{
			initnode(g[gNb],$ST.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$ST.text->chars);
			$nodeNb = gNb;
			gNb++;}  
		| BT
		{
			initnode(g[gNb],$BT.text->chars, 2, gNb); 
			printf("node \%d: \%s\n",gNb,$BT.text->chars);
			$nodeNb = gNb;
			gNb++;}  
		)  b2 = bsub3factor 
		{
			c[cNb] = g[$b1.nodeNb];
			c[cNb+1] = g[$b2.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);			
		}
		)*;	
	
bsub3factor returns [int nodeNb]
@init {
	int lnotExist = 0, i;
}
	:	(LNOT {
			lnotExist = gNb;
			gNb++;
		})? 
		(TRUE {
			initnode(g[gNb],$TRUE.text->chars, 4, gNb); 
			printf("node \%d: \%s\n",gNb,$TRUE.text->chars);
			$nodeNb = gNb;
			gNb++;}
		| FALSE {
			initnode(g[gNb],$FALSE.text->chars, 4, gNb); 
			printf("node \%d: \%s\n",gNb,$FALSE.text->chars);
			$nodeNb = gNb;
			gNb++;}
		| ID {
			struct node* temp = (struct node*)malloc(sizeof(struct node)), *hashNode = (struct node*)malloc(sizeof(struct node));
			temp = linearProbingSearch(h,$ID.text->chars);
			if (temp=='\0') {
				printf("Line \%d, Position \%d : Unknown identifier \%s!\n",$ID.line, $ID.pos,$ID.text->chars);
				exit(1);
			}
			else if (IDMatrix[temp->sequence][scopeCounter]==-1) {
				i = scopeCounter-1;
				while (i >= 0) {
					if (IDMatrix[temp->sequence][i]!=-1) {
						$nodeNb = IDMatrix[temp->sequence][i];
						printf("node \%d: \%s\n",$nodeNb,$ID.text->chars);
						break;
					}
					i--;
				}	
				if (i < 0) {
					printf("Line \%d, Position \%d : Unknown identifier \%s!\n",$ID.line, $ID.pos,$ID.text->chars);
					exit(1);
				}
			}
			else {
				$nodeNb = IDMatrix[temp->sequence][scopeCounter];
				printf("node \%d: \%s\n",$nodeNb,$ID.text->chars);			
			}
			}			
		| NUMBER {
			initnode(g[gNb],$NUMBER.text->chars, 0, gNb); 
			printf("node \%d: \%s\n",gNb,$NUMBER.text->chars);
			$nodeNb = gNb;
			gNb++;}
		| funcall)
		{
			if (lnotExist)
			{
				initnode(g[lnotExist],$LNOT.text->chars, 2, lnotExist); 
				printf("node \%d: \%s\n",gNb,$LNOT.text->chars);
				$nodeNb = lnotExist;
				c[cNb] = g[lnotExist+1];
				c[cNb+2] = '\0';
				addnode('\0',c,g[lnotExist]);				
			}
		};


ifstatement returns [int nodeNb]
@init {
scopeCounter++;}
@after {
scopeCounter--;}

	:	IF {
			initnode(g[gNb],$IF.text->chars, 3, gNb); 
			printf("node \%d: \%s\n",gNb,$IF.text->chars);
			$nodeNb = gNb;
			gNb++;}
		OPPAREN b=bexpr CLPAREN
		OPBRACE (st1=statementlist)? CLBRACE 
		(ELSE OPBRACE {
			scopeCounter++;
		}	
		(st2=statementlist)? CLBRACE
		{
			scopeCounter--;
		})?
		{	
			c[cNb] = g[$b.nodeNb];
			c[cNb+1] = g[$st1.nodeNb];
			if ($st2.nodeNb != 0) {
				c[cNb+2] = g[$st2.nodeNb];
				c[cNb+3] = '\0';
			}
			else
				c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);
		};
		
whilestatement returns [int nodeNb]
@init  {
scopeCounter++;}
@after {
scopeCounter--;}
	:	WHILE {
			initnode(g[gNb],$whilestatement.text->chars, 3, gNb); 
			printf("node \%d: \%s\n",gNb,$whilestatement.text->chars);
			$nodeNb = gNb;
			gNb++;
		}
		OPPAREN bexpr CLPAREN 
		OPBRACE (statementlist)? CLBRACE
		{
			c[cNb] = g[$bexpr.nodeNb];
			c[cNb+1] = g[$statementlist.nodeNb];
			c[cNb+2] = '\0';
			addnode('\0',c,g[$nodeNb]);
		};

forstatement
@init {
scopeCounter++;}
@after {
scopeCounter--;}
	:	FOR OPPAREN fordec SEMIC bexpr SEMIC (assignment)? CLPAREN 
		OPBRACE (statementlist)? CLBRACE;


fundec	:	(INT | BOOL | VOID) ID OPPAREN (paramdec (COMMA paramdec)*)? CLPAREN SEMIC;

fundef	
@init {
scopeCounter++;}
@after {
scopeCounter--;}
	:	(INT | BOOL | VOID) ID OPPAREN (paramdec (COMMA paramdec)*)? CLPAREN 
		OPBRACE 
		(statementlist)?
		RETURN (expr | TRUE | FALSE)? SEMIC 
		CLBRACE;

funcall	:	ID OPPAREN (expr (COMMA expr)*)? CLPAREN;

/*------------------------------------------------------------------
 * LEXER RULES
 *------------------------------------------------------------------*/

fragment DIGIT
	:	'0'..'9';

fragment USCORE
	:	'_';

fragment CLETTER
	:	'A'..'Z';

fragment SLETTER
	:	'a'..'z';
	
NUMBER	:	(MINUS)? (DIGIT)+;

ID	:	(USCORE)* (SLETTER | CLETTER) (SLETTER | CLETTER | NUMBER | USCORE)*;
   
COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
    |   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;};

WS  :   ( ' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};